<template>
  <div
    class="global-virtual-list-root"
    ref="virtualList"
    v-loading="loading"
  >
    <a-list
      class="height-100-percent"
      :virtual-list-props="{
        height: height,
        buffer: columnCount,
        fixedSize: true,
        estimatedSize: estimatedSize || itemHeight,
      }"
      :data="calcList"
      :scrollbar="false"
      @reach-bottom="onReachBottom"
    >
      <template #item="{ item, index }">
        <a-list-item :key="index">
          <div class="list-flex">
            <div
              class="list-card-wrap"
              v-for="(card, ind) in item"
              :key="ind + 'card'"
              :style="{ width: cardWidth }"
            >
              <slot
                :data="card"
                :index="ind"
              />
            </div>
            <i
              v-for="i in rowCount - item.length"
              :key="i"
              class="item-placement"
              :style="{ width: cardWidth }"
            />
          </div>
        </a-list-item>
      </template>

      <template #empty>
        <slot
          name="empty"
          v-if="!loading"
        >
          <titan-empty />
        </slot>
      </template>
    </a-list>
  </div>
</template>

<script setup lang="ts">
  import { nextTick, onBeforeUnmount, onMounted, ref, watch, computed } from "vue";
  import { IProp, IPageForm } from "@/components/virtual-list/src/types";
  import { debounce } from "lodash-es";
  import useVirtualList from "@/hooks/useVirtualList";

  defineOptions({
    name: "VirtualList",
  });
  const props = withDefaults(defineProps<IProp>(), {
    itemWidth: 240, // 卡片宽度
    itemHeight: 120, // 卡片高度
    pageType: "page",
    searchParams: () => ({}),
    immediate: true,
    responseFilter: (res: any) => {
      return {
        list: res?.list || [],
        total: res?.total || 0,
      };
    },
    extraList: () => [],
    manual: false,
    estimatedSize: 0, // 不等高卡片预估高度
    rowCount: 0, // 单行展示数量
  });

  const emits = defineEmits(["change"]);

  const list = ref<any>([]);
  const loading = ref<boolean>(false);
  const disabled = ref<boolean>(false);
  const searchAccess = ref<boolean>(false);
  const pageForm = ref<IPageForm>({
    page: 0,
    from: 0,
    scroll_array: null,
  });

  const defaultParams = { size: 20 };

  const { rowCount, columnCount, calcList, virtualList, resize, height } = useVirtualList(list, {
    itemWidth: props.itemWidth,
    itemHeight: props.itemHeight,
    extraList: props.extraList,
    rowCount: props.rowCount,
  });

  const cardWidth = computed(() => {
    if (props.rowCount > 0) {
      return `calc((100% - ${(props.rowCount - 1) * 20 + "px"}) / ${props.rowCount})`;
    }
    return props.itemWidth + "px";
  });

  const debounceFetchData = debounce(() => {
    onReachBottom();
  }, 500);

  const handleSearch = () => {
    searchAccess.value = true;
    list.value = [];
    disabled.value = false;
    if (props.pageType === "page") {
      pageForm.value.page = 0;
    }
    if (props.pageType === "from") {
      pageForm.value.from = 0;
    }
    if (props.pageType === "scroll_array") {
      pageForm.value.scroll_array = null;
    }
    debounceFetchData();
  };
  const onReachBottom = async () => {
    if (loading.value || disabled.value || (!props.immediate && !searchAccess.value) || !props.request) return;
    try {
      loading.value = true;
      let tempSearch = { ...defaultParams, ...props.searchParams, [props.pageType]: pageForm.value[props.pageType as keyof typeof pageForm.value] };
      if (props.requestFilter) {
        tempSearch = props.requestFilter(tempSearch);
      }
      const res = await props.request(tempSearch);
      const _resp = await props.responseFilter(res);
      list.value.push(..._resp.list);
      emits("change", _resp.list, list.value, _resp.total);
      if (props.pageType === "page") {
        pageForm.value.page += 1;
      }
      if (props.pageType === "from") {
        pageForm.value.from += tempSearch.size;
      }
      if (props.pageType === "scroll_array") {
        pageForm.value.scroll_array = res.scroll_array || null;
      }
      disabled.value = list.value.length >= _resp.total;
    } finally {
      loading.value = false;
      if (list.value.length && calcList.value.length < columnCount.value) {
        await nextTick();
        await onReachBottom();
      }
    }
  };

  watch(
    () => props.searchParams,
    () => {
      if (props.manual) return;
      handleSearch();
    },
    { deep: true }
  );

  onMounted(() => {
    resize();
    window.addEventListener("resize", resize);
  });

  onBeforeUnmount(() => {
    window.removeEventListener("resize", resize);
  });

  defineExpose({
    handleSearch,
  });
</script>
